import os
import logging
import uuid
from typing import Dict, Any, BinaryIO, Optional
import boto3
from botocore.config import Config
from botocore.exceptions import ClientError

# 获取环境变量中的S3配置
from django.conf import settings

# 从settings.py中获取S3配置
S3_CONFIG = getattr(settings, 'S3_CONFIG', {})

logger = logging.getLogger('s3')


class S3Service:
    """S3对象存储服务，提供文件上传、下载、签名URL等功能"""

    def __init__(self):
        """初始化S3服务连接"""
        # 强制使用S3，不再提供本地存储选项
        self.use_s3 = True
        self.bucket_name = S3_CONFIG.get('S3_BUCKET_NAME', 'jianying-drafts')
        self.region_name = S3_CONFIG.get('S3_REGION_NAME', 'auto')
        self.endpoint_url = S3_CONFIG.get('S3_ENDPOINT_URL')
        self.public_url_prefix = S3_CONFIG.get('PUBLIC_URL_PREFIX', '')
        self.default_acl = S3_CONFIG.get('DEFAULT_ACL', 'public-read')
        self.default_expires_in = S3_CONFIG.get('DEFAULT_EXPIRES_IN', 3600)
        self.default_cache_control = S3_CONFIG.get('DEFAULT_CACHE_CONTROL')
        
        try:
            # 创建S3客户端
            self.s3_client = boto3.client(
                's3',
                aws_access_key_id=S3_CONFIG.get('AWS_ACCESS_KEY_ID'),
                aws_secret_access_key=S3_CONFIG.get('AWS_SECRET_ACCESS_KEY'),
                region_name=self.region_name,
                endpoint_url=self.endpoint_url,
                config=Config(signature_version='s3v4')
            )
            
            # 验证连接
            try:
                self.s3_client.head_bucket(Bucket=self.bucket_name)
                logger.info(f"✅ 成功连接到S3存储桶: {self.bucket_name}")
            except ClientError as e:
                error_code = e.response.get('Error', {}).get('Code', 'Unknown')
                if error_code == '404':
                    logger.error(f"❌ 存储桶 '{self.bucket_name}' 不存在，请先创建")
                    raise ValueError(f"S3存储桶 '{self.bucket_name}' 不存在")
                elif error_code == '403':
                    logger.error(f"❌ 无权限访问存储桶 '{self.bucket_name}'，请检查凭证权限")
                    raise ValueError(f"无权限访问S3存储桶 '{self.bucket_name}'")
                else:
                    logger.error(f"❌ 无法连接到S3存储桶: {str(e)}")
                    raise e
        except Exception as e:
            logger.error(f"❌ 初始化S3客户端失败: {str(e)}")
            raise ValueError(f"S3服务初始化失败: {str(e)}")
    
    def upload_file(self, file_obj: BinaryIO, object_key: str, content_type: str = 'application/octet-stream', public: bool = False, expires_in: Optional[int] = None) -> Dict[str, Any]:
        """
        上传文件到S3存储
        
        Args:
            file_obj: 文件对象
            object_key: 对象键（路径）
            content_type: 内容类型
            public: 是否公开访问
            expires_in: 预签名URL过期时间（秒），默认为配置中的DEFAULT_EXPIRES_IN
            
        Returns:
            dict: 上传结果信息
        """
        try:
            # 确定额外参数
            extra_args = {
                'ContentType': content_type
            }
            
            # 视频预览文件默认公开访问
            if object_key.startswith('previews/'):
                public = True
                logger.info(f"🔒 视频预览文件将设置为公开访问: {object_key}")
            
            # 如果是公开访问，添加ACL参数
            if public:
                extra_args['ACL'] = self.default_acl
            
            # 如果指定了缓存控制，添加相应参数
            if self.default_cache_control:
                extra_args['CacheControl'] = self.default_cache_control
            
            # 上传文件
            file_obj.seek(0)
            self.s3_client.upload_fileobj(
                file_obj,
                self.bucket_name,
                object_key,
                ExtraArgs=extra_args
            )
            
            # 获取文件URL
            file_url = None
            
            # 对于公开访问的文件，优先使用PUBLIC_URL_PREFIX
            if public:
                # 使用自定义域名前缀（如果有）
                if self.public_url_prefix:
                    # 确保不会有多余的斜杠
                    base_url = self.public_url_prefix.rstrip('/')
                    file_url = f"{base_url}/{object_key}"
                    logger.info(f"🔗 使用自定义公共URL: {file_url}")
                else:
                    # 对于Cloudflare R2，使用pub-格式的URL
                    if self.endpoint_url and 'cloudflarestorage.com' in self.endpoint_url:
                        bucket_endpoint = self.endpoint_url.replace('https://', 'https://pub-')
                        file_url = f"{bucket_endpoint}/{self.bucket_name}/{object_key}"
                    # 对于标准S3，使用不含签名的URL
                    else:
                        file_url = f"https://{self.bucket_name}.s3.{self.region_name}.amazonaws.com/{object_key}"
            
            # 如果不是公开访问或者无法构建公开URL，生成预签名URL
            if not file_url:
                expiration = expires_in or self.default_expires_in or 3600
                file_url = self.s3_client.generate_presigned_url(
                    'get_object',
                    Params={
                        'Bucket': self.bucket_name,
                        'Key': object_key
                    },
                    ExpiresIn=expiration
                )
            
            logger.info(f"✅ 文件上传到S3成功: {object_key}")
            logger.info(f"🔗 文件访问URL: {file_url}")
            
            return {
                'success': True,
                'file_url': file_url,
                'object_key': object_key,
                'is_public': public,
                'storage_type': 's3'
            }
        
        except Exception as e:
            logger.error(f"❌ S3上传失败: {str(e)}")
            raise ValueError(f"文件上传到S3失败: {str(e)}")
    
    def get_file_url(self, object_key: str, expires_in: Optional[int] = None) -> str:
        """
        获取文件的URL
        
        Args:
            object_key: 对象键（路径）
            expires_in: 预签名URL过期时间（秒），默认为配置中的DEFAULT_EXPIRES_IN
            
        Returns:
            str: 文件URL
        """
        # 尝试构建公开URL
        if self.public_url_prefix:
            return f"{self.public_url_prefix.rstrip('/')}/{object_key}"
        
        # 生成预签名URL
        expiration = expires_in or self.default_expires_in or 3600
        return self.s3_client.generate_presigned_url(
            'get_object',
            Params={
                'Bucket': self.bucket_name,
                'Key': object_key
            },
            ExpiresIn=expiration
        )
    
    def delete_file(self, object_key: str) -> bool:
        """
        删除文件
        
        Args:
            object_key: 对象键（路径）
            
        Returns:
            bool: 是否删除成功
        """
        try:
            self.s3_client.delete_object(
                Bucket=self.bucket_name,
                Key=object_key
            )
            logger.info(f"✅ 文件从S3删除成功: {object_key}")
            return True
        except Exception as e:
            logger.error(f"❌ 从S3删除文件失败: {str(e)}")
            return False
